#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <GL/freeglut.h>
#include <juanmabc.h>

unsigned int width, height, str[3], mdl[2], action[2], tag, loop[2];
float rot[3], rrot[3], scl[3], rscl[3], tgm[16];
typedef enum { CMESH, MESH, CMODEL, MODEL } ModelType;
ModelType type;

void
jmbcBlowIn(const char *mode, const char *folder, const char *model, const char *submodel)
{
	jmbcCreateContext();

	glEnable(GL_DEPTH_TEST);

	glShadeModel(GL_SMOOTH);

	glEnable(GL_TEXTURE_2D);

	glEnable(GL_BLEND);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
	glLoadIdentity();

	if(!strcmp(mode, "CMesh"))
		type = CMESH;
	else if(!strcmp(mode, "Mesh"))
		type = MESH;
	else if(!strcmp(mode, "CModel"))
		type = CMODEL;
	else if(!strcmp(mode, "Model"))
		type = MODEL;

	tlGenStrings(3, str);
	tlStringLoadC(str[0], folder);
	tlStringLoadC(str[1], model);
	tlStringLoadC(str[2], submodel);

	switch(type)
	{
		case CMESH:
			mlGenMeshes(1, mdl);
			mlMeshLoad(mdl[0], str[1]);
			mlMeshGenMaterials(mdl[0], str[0]);
		break;
		case MESH:
			mlGenMeshes(1, mdl);
			mlMeshCompile(mdl[0], str[1]);
			mlMeshGenMaterials(mdl[0], str[0]);
		break;
		case CMODEL:
			mlGenModels(1, mdl);
			mlModelLoad(mdl[0], str[1]);
			mlModelGenMaterials(mdl[0], str[0]);
			if(mlModelTags(mdl[0]) > 0)
			{
				tag = 0;
			}
			if(mlModelActions(mdl[0]) > 0)
			{
				action[0] = 0;
				mlModelActionSet(mdl[0], action[0]);
				mlModelActionFrameSet(mdl[0], 0);

				mlGenLoops(1, loop);
				mlLoopLoad(loop[0], 0.0f, (float) mlModelActionFrames(mdl[0], action[0]), 1.0f, 0.0f);
			}
		break;
		case MODEL:
			mlGenModels(1, mdl);
			mlModelCompile(mdl[0], str[1]);
			mlModelGenMaterials(mdl[0], str[0]);
			if(mlModelTags(mdl[0]) > 0)
			{
				tag = 0;
			}
			if(mlModelActions(mdl[0]) > 0)
			{
				action[0] = 0;
				mlModelActionSet(mdl[0], action[0]);
				mlModelActionFrameSet(mdl[0], 0);

				mlGenLoops(1, loop);
				mlLoopLoad(loop[0], 0.0f, (float) mlModelActionFrames(mdl[0], action[0]), 1.0f, 0.0f);
			}
		break;
	}

	mlPointLoadZero(rot);
	mlPointLoadZero(rrot);
	mlVectorLoad(scl, 1.0f, 1.0f, 1.0f);
	mlPointLoadZero(rscl);
}

void
jmbcBlowOut(void)
{
	switch(type)
	{
		case CMESH:
		case MESH:
			mlMeshUnload(mdl[0]);
			mlMeshDeleteMaterials(mdl[0]);
			mlDeleteMeshes(1, mdl);
		break;
		case CMODEL:
		case MODEL:
			mlDeleteLoops(1, loop);
			mlModelDeleteMaterials(mdl[0]);
			mlModelUnload(mdl[0]);
			mlDeleteModels(1, mdl);
		break;
	}

	tlStringsUnload(3, str);
	tlDeleteStrings(3, str);

	jmbcDestroyContext();
}

void
jmbcReshape(int w, int h)
{
	width = w; height = h;
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0f, (float) width / (float) height, 0.1f, 1000.0f);
	glMatrixMode(GL_MODELVIEW);
}
 

void
jmbcBlow(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	gluLookAt(0, 0, 100, 0, 0, 0, 0, 1, 0);

	mlVectorAdd(rot, rrot);
	glRotatef(rot[0], 1.0f, 0.0f, 0.0f);
	glRotatef(rot[1], 0.0f, 1.0f, 0.0f);
	glRotatef(rot[2], 0.0f, 0.0f, 1.0f);
	mlVectorAdd(scl, rscl);
	glScalef(scl[0], scl[1], scl[2]);

	switch(type)
	{
		case CMESH:
		case MESH:
			glPushMatrix();
			//glRotatef(-90, 1.0f, 0.0f, 0.0f);
			mlMeshDraw(mdl[0]);
			glPopMatrix();
		break;
		case CMODEL:
		case MODEL:
			glPushMatrix();
			//glRotatef(-90, 1.0f, 0.0f, 0.0f);
			mlModelDraw(mdl[0]);
	
			/*
			glPushMatrix();
			mlModelTagMatrix(mdl[0], tag, action[0], (unsigned int) mlLoopValue(loop[0]), tgm);

			glMultMatrixf(tgm);
			mlModelDraw(mdl[1], txs[1], action[1], (unsigned int) mlLoopValue(loop[1]));
			glPopMatrix();
			*/

			glPopMatrix();

			if(mlModelActions(mdl[0]) > 0)
			{
				mlLoopInc(loop[0]);
				mlModelActionFrameSet(mdl[0], (unsigned int) mlLoopValue(loop[0]));
			}
		break;
	}

	glutSwapBuffers();
}

void
jmbcKeyboard(unsigned char key, int x, int y)
{
	switch(type)
	{
		case CMESH:
		case MESH:
		break;
		case CMODEL:
		case MODEL:
			switch(key)
			{
				case 32:
					action[0] = (action[0] + 1) % mlModelActions(mdl[0]);
					mlModelActionSet(mdl[0], action[0]);
					mlModelActionFrameSet(mdl[0], 0);
					mlLoopLoad(loop[0], 0.0f, (float) mlModelActionFrames(mdl[0], action[0]), 1.0f, 0.0f);
				break;
			}
		break;
	}
}

void
jmbcKeyboardUp(unsigned char key, int x, int y)
{
	if(key == 27) glutLeaveMainLoop();
}

void
jmbcSpecial(int key, int x, int y)
{
	switch(key)
	{
		case GLUT_KEY_UP:
			mlVectorLoad(rscl, 0.1f, 0.1f, 0.1f);
		break;
		case GLUT_KEY_DOWN:
			mlVectorLoad(rscl, -0.1f, -0.1f, -0.1f);
		break;
		case GLUT_KEY_LEFT:
			rrot[1] = 1.0f;
		break;
		case GLUT_KEY_RIGHT:
			rrot[1] = -1.0f;
		break;
	}
}

void
jmbcSpecialUp(int key, int x, int y)
{
	switch(key)
	{
		case GLUT_KEY_UP:
			mlPointLoadZero(rscl);
		break;
		case GLUT_KEY_DOWN:
			mlPointLoadZero(rscl);
		break;
		case GLUT_KEY_LEFT:
			rrot[1] = 0.0f;
		break;
		case GLUT_KEY_RIGHT:
			rrot[1] = 0.0f;
		break;
	}
}

void
jmbcMouseButton(int button, int state, int x, int y)
{
	switch(button)
	{
		case GLUT_LEFT_BUTTON:
			rrot[1] = (float) (state == GLUT_DOWN);
		break;
		case GLUT_RIGHT_BUTTON:
			rrot[1] = - (float) (state == GLUT_DOWN);
		break;
	}
}

void
jmbcTimer(int value)
{
	glutPostRedisplay();
	glutTimerFunc(20, jmbcTimer, 0);
}

int
main(int argc, char **argv)
{
	if(argc == 4)
	{
		int tmpargc = 0; 
		char *tmpargv[] = { "ModelV", NULL };

		glutInit(&tmpargc, tmpargv);
		glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
		width = 800; height = 600;
		glutInitWindowSize(width, height);
		glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH) - width) / 2, (glutGet(GLUT_SCREEN_HEIGHT) - height) / 2);
		glutCreateWindow(tmpargv[0]);

		glutDisplayFunc(jmbcBlow);
		glutReshapeFunc(jmbcReshape);
		glutKeyboardFunc(jmbcKeyboard);
		glutKeyboardUpFunc(jmbcKeyboardUp);
		glutSpecialFunc(jmbcSpecial);
		glutSpecialUpFunc(jmbcSpecialUp);
		glutMouseFunc(jmbcMouseButton);
		glutTimerFunc(20, jmbcTimer, 0);

		jmbcBlowIn(argv[1], argv[2], argv[3], "Don't care");
		atexit(jmbcBlowOut);

		glutMainLoop();
	}
	else
		wprintf(L"%hs 'CMesh/Mesh/CModel/Model' DataFolder DataFile\n", argv[0]);

	return EXIT_SUCCESS;
}
